/**
 * Copyright (c) 2015-2030 by Beijing Yanlight Co., Ltd.
 * All rights reserved.
 */
package com.yanlight.app.module.partner.service.impl;

import com.yanlight.app.dal.dao.*;
import com.yanlight.app.dal.dto.AdminPageQuery;
import com.yanlight.app.dal.dto.PaycodeQuery;
import com.yanlight.app.dal.entity.*;
import com.yanlight.app.model.PayCodeSpec;
import com.yanlight.app.module.partner.request.*;
import com.yanlight.app.module.partner.response.*;
import com.yanlight.app.module.partner.response.MerchantSubsResponse.SubAccount;
import com.yanlight.app.module.partner.service.PartnMerchantService;
import com.yanlight.exception.ErrorType;
import com.yanlight.exception.TradeException;
import com.yanlight.support.http.Response;
import com.yanlight.type.StatusType;
import com.yanlight.type.UserRootRole;
import com.yanlight.util.DateTimeUtil;
import com.yanlight.util.EncryptUtil;
import com.yanlight.util.ResponseUtil;
import com.yanlight.util.ToolsUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @author haolw
 * @time 2018/07/04
 */
@Service
public class PartnMerchantServiceImpl implements PartnMerchantService {
    @Autowired
    private UserDAO userDAO;
    @Autowired
    private CompanyDAO companyDAO;
    @Autowired
    private WxpayConfigDAO wxpayConfigDAO;
    @Autowired
    private AlipayConfigDAO alipayConfigDAO;
    @Autowired
    private PaycodeDAO paycodeDAO;
    @Autowired
    private AdminBindDAO bindDAO;

    @Override
    public MerchantDetailResponse findDetail(Integer userId) {
        MerchantDetailResponse response = new MerchantDetailResponse();
        Admin admin = userDAO.findById(userId);
        Company company = companyDAO.findById(admin.getCompanyId());
        WxpayConfig wxpayConfig = wxpayConfigDAO.findByCompanyId(company.getId());
        AlipayConfig alipayConfig = alipayConfigDAO.findByCompanyId(company.getId());
        response.setId(userId);
        response.setPhone(admin.getPhone());
        response.setEmail(admin.getEmail());
        response.setCompanyName(company.getName());
        response.setCompanyShortName(company.getShortName());
        response.setCompanyAddress(company.getAddress());
        response.setCreateTime(admin.getCreateTime());
        if (wxpayConfig != null) {
            response.setWxpayStatus(wxpayConfig.getStatus());
        }
        if (alipayConfig != null) {
            response.setAlipayStatus(alipayConfig.getStatus());
        }
        response.setCompanyLogo(ToolsUtil.wrapImage(company.getLogo()));
        return response;
    }

    @Override
    public MerchantPaycodePageResponse findPagePaycodes(MerchantPaycodesQueryRequest request) {
        MerchantPaycodePageResponse response = new MerchantPaycodePageResponse();

        Integer userId = request.getUserId();
        Admin admin = userDAO.findById(userId);

        PaycodeQuery query = new PaycodeQuery();
        BeanUtils.copyProperties(request, query);
        query.setCompanyId(admin.getCompanyId());

        int count = paycodeDAO.count(query);

        if (count > 0) {
            List<PayCode> paycodes = paycodeDAO.findPage(query);
            if (!CollectionUtils.isEmpty(paycodes)) {
                PayCodeSpec spec;
                for (PayCode payCode : paycodes) {
                    spec = new PayCodeSpec();
                    BeanUtils.copyProperties(payCode, spec);
                    spec.setQrCode(ToolsUtil.encryptPaycode(payCode.getValue()));
                    response.addPaycodeSpec(spec);
                }
            }
        }
        response.setPageSize(request.getPageSize());
        response.setTotalRecord(count);
        return response;
    }

    @Override
    public MerchantPaycodesAvailableResponse findAvailablePaycodes(Integer userId) {
        MerchantPaycodesAvailableResponse response = new MerchantPaycodesAvailableResponse();
        Admin admin = userDAO.findById(userId);
        List<PayCode> paycodeAvailables = paycodeDAO.findAvailables(admin.getCompanyId());
        if (!CollectionUtils.isEmpty(paycodeAvailables)) {
            List<Integer> paycodeIds = new ArrayList<Integer>();
            for (PayCode payCode : paycodeAvailables) {
                paycodeIds.add(payCode.getId());
            }

            List<AdminBind> binds = bindDAO.findByPaycodeIds(paycodeIds);
            List<Integer> bindedPaycodeIds = new ArrayList<Integer>();
            if (!CollectionUtils.isEmpty(binds)) {
                for (AdminBind bind : binds) {
                    bindedPaycodeIds.add(bind.getPayCodeId());
                }
            }
            Iterator<PayCode> iterator = paycodeAvailables.iterator();
            while (iterator.hasNext()) {
                PayCode payCode = iterator.next();
                Integer payCodeId = payCode.getId();
                if (bindedPaycodeIds.contains(payCodeId)) {
                    iterator.remove();
                }
            }

            if (!CollectionUtils.isEmpty(paycodeAvailables)) {
                PayCodeSpec spec;
                for (PayCode payCode : paycodeAvailables) {
                    spec = new PayCodeSpec();
                    BeanUtils.copyProperties(payCode, spec);
                    spec.setQrCode(ToolsUtil.encryptPaycode(payCode.getValue()));
                    response.addPaycodeSpec(spec);
                }
            }
        }
        return response;
    }

    @Override
    public void addPaycode(MerchantPaycodeAddRequest request) {
        Integer userId = request.getUserId();
        Integer status = request.getStatus();
        String alias = request.getAlias();
        Admin admin = userDAO.findById(userId);
        PayCode paycode = new PayCode();
        paycode.setAlias(alias);

        //管理员提价未绑定公司的支付码
        if (admin.getId() != 1) {
            paycode.setCompanyId(admin.getCompanyId());
        }

        paycode.setStatus(status);
        paycode.setValue(EncryptUtil.md5_16(ToolsUtil.uuid()));
        paycode.setCreateTime(DateTimeUtil.getNowTime());
        paycodeDAO.save(paycode);
    }

    @Override
    public void modifyPaycode(MerchantPaycodeModifyRequest request) {
        Integer id = request.getId();
        Integer status = request.getStatus();
        String alias = request.getAlias();
        if (StringUtils.isBlank(alias) && status == null) {
            return;
        } else {
            PayCode paycode = new PayCode();
            paycode.setId(id);
            paycode.setAlias(alias);
            paycode.setStatus(status);
            paycodeDAO.modify(paycode);
        }
    }

    @Override
    public MerchantPaycodeResponse findPaycode(Integer id) {
        MerchantPaycodeResponse response = new MerchantPaycodeResponse();
        PayCode payCode = paycodeDAO.findById(id);
        if (payCode != null) {
            response.setId(id);
            response.setAlias(payCode.getAlias());
            response.setStatus(payCode.getStatus());
            response.setValue(ToolsUtil.encryptPaycode(payCode.getValue()));
        }
        return response;
    }

    @Override
    public MerchantSubsResponse findPageSubs(MerchantSubPageQueryRequest request) {
        Integer userId = request.getUserId();
        String email = request.getEmail();
        String phone = request.getPhone();
        MerchantSubsResponse response = new MerchantSubsResponse();
        Admin admin = userDAO.findById(userId);
        Integer companyId = admin.getCompanyId();
        AdminPageQuery query = new AdminPageQuery();
        query.setEmail(email);
        query.setPhone(phone);
        query.setCompanyId(companyId);
        query.setRoot(UserRootRole.PARTNER_SUB.getType());
        int count = userDAO.count(query);
        if (count > 0) {
            List<Admin> users = userDAO.find(query);
            Set<Integer> userIds = new HashSet<Integer>();
            for (Admin user : users) {
                userIds.add(user.getId());
            }
            List<AdminBind> bindList = bindDAO.findByUserIds(userIds);
            Map<Integer, PayCode> bindMap = new HashMap<Integer, PayCode>();
            if (!CollectionUtils.isEmpty(bindList)) {
                Set<Integer> paycodeIds = new HashSet<Integer>();
                for (AdminBind bind : bindList) {
                    Integer payCodeId = bind.getPayCodeId();
                    if (payCodeId != null) {
                        paycodeIds.add(payCodeId);
                    }
                }
                if (!CollectionUtils.isEmpty(paycodeIds)) {
                    List<PayCode> paycodes = paycodeDAO.findByIds(paycodeIds);
                    Map<Integer, PayCode> payCodesMap = new HashMap<Integer, PayCode>();
                    for (PayCode payCode : paycodes) {
                        payCodesMap.put(payCode.getId(), payCode);
                    }
                    for (AdminBind bind : bindList) {
                        Integer payCodeId = bind.getPayCodeId();
                        if (payCodeId != null) {
                            PayCode payCode = payCodesMap.get(payCodeId);
                            if (payCode != null) {
                                bindMap.put(bind.getAdminId(), payCode);
                            }
                        }
                    }
                }
            }

            SubAccount subAccount = null;
            for (Admin user : users) {
                PayCode payCode = bindMap.get(user.getId());
                subAccount = response.new SubAccount();
                subAccount.setId(user.getId());
                subAccount.setEmail(user.getEmail());
                subAccount.setPhone(user.getPhone());
                subAccount.setStatus(user.getStatus());
                String paycodeAlias = "", paycodeValue = "";
                if (payCode != null) {
                    paycodeAlias = payCode.getAlias();
                    paycodeValue = ToolsUtil.encryptPaycode(payCode.getValue());
                }
                subAccount.setPaycodeAlias(paycodeAlias);
                subAccount.setPaycodeValue(paycodeValue);
                response.addSubAccount(subAccount);
            }
        }
        response.setPageSize(request.getPageSize());
        response.setTotalRecord(count);
        return response;
    }

    @Override
    @Transactional
    public void addSub(MerchantSubAddRequest request) {
        Integer userId = request.getUserId();
        String email = request.getEmail();
        String phone = request.getPhone();
        String password = request.getPassword();
        Integer paycodeId = request.getPaycodeId();
        Admin user = userDAO.findByEmailOrPhone(email, phone);
        if (user != null) {
            throw TradeException.error(ErrorType.PHONE_EMAIL_REGISTERED);
        }
        if (paycodeId != null) {
            AdminBind adminBind = bindDAO.findByPaycodeId(paycodeId);
            if (adminBind != null) {
                throw TradeException.error(ErrorType.DUPLICATION_BINDING);
            }
        }
        Admin admin = userDAO.findById(userId);
        String nowTime = DateTimeUtil.getNowTime();
        user = new Admin();
        user.setPhone(phone);
        user.setEmail(email);
        user.setPassword(EncryptUtil.md5AndSalt(password));
        user.setStatus(request.getStatus());
        user.setCreateTime(nowTime);
        user.setCompanyId(admin.getCompanyId());
        user.setRoot(UserRootRole.PARTNER_SUB.getType());
        userDAO.save(user);
        if (paycodeId != null) {
            AdminBind bind = new AdminBind();
            bind.setAdminId(user.getId());
            bind.setPayCodeId(paycodeId);
            bind.setCreateTime(nowTime);
            bind.setStatus(StatusType.ACTIVITY.ordinal());
            bindDAO.save(bind);
        }
    }

    @Override
    @Transactional
    public void modifySub(MerchantSubModifyRequest request) {
        Integer id = request.getId();
        String password = request.getPassword();
        Integer paycodeId = request.getPaycodeId();
        Admin user = userDAO.findById(id);
        if (user != null) {
            if (StringUtils.isNotBlank(password)) {
                userDAO.updatePassword(id, EncryptUtil.md5AndSalt(password));
            }
            if (request.getStatus() != null) {
                userDAO.updateStatus(id, request.getStatus());
            }
            AdminBind bind = bindDAO.findByUserId(id);
            Integer storagePaycodeId = bind == null ? null : bind.getPayCodeId();
            if (storagePaycodeId == null && paycodeId != null) {
                if (bind == null) {
                    bind = new AdminBind();
                    bind.setAdminId(user.getId());
                    bind.setPayCodeId(paycodeId);
                    bind.setCreateTime(DateTimeUtil.getNowTime());
                    bind.setStatus(StatusType.ACTIVITY.ordinal());
                    bindDAO.save(bind);
                } else {
                    AdminBind binded = bindDAO.findByPaycodeId(paycodeId);
                    if (binded != null) {
                        throw TradeException.error(ErrorType.DUPLICATION_BINDING);
                    }
                    bindDAO.bindPaycode(id, paycodeId);
                }

            } else if (storagePaycodeId != null && paycodeId == null) {
                bindDAO.unBindPaycode(bind.getId());
            } else if (storagePaycodeId != null && paycodeId != null) {
                if (!storagePaycodeId.equals(paycodeId)) {
                    AdminBind binded = bindDAO.findByPaycodeId(paycodeId);
                    if (binded != null) {
                        throw TradeException.error(ErrorType.DUPLICATION_BINDING);
                    }
                    bindDAO.bindPaycode(id, paycodeId);
                }
            }
        }
    }

    @Override
    public MerchantSubDetailResponse findSubDetail(Integer id) {
        MerchantSubDetailResponse resposne = new MerchantSubDetailResponse();
        Admin user = userDAO.findBySubId(id);
        if (user != null) {
            AdminBind bind = bindDAO.findByUserId(id);
            if (bind != null && bind.getPayCodeId() != null) {
                PayCode payCode = paycodeDAO.findById(bind.getPayCodeId());
                resposne.setPaycodeId(bind.getPayCodeId());
                resposne.setPaycodeAlias(payCode.getAlias());
            }
            resposne.setId(id);
            resposne.setEmail(user.getEmail());
            resposne.setPhone(user.getPhone());
            resposne.setStatus(user.getStatus());
            resposne.setPaycodes(findAvailablePaycodes(id).getPaycodes());
        }
        return resposne;
    }

    @Override
    public Response bindCompany(Integer codeId, Integer companyId) {

        if (codeId == null || companyId == null) {
            return ResponseUtil.fail(ErrorType.UNLAWFUL_PARAMETERS);
        }

        Company company = companyDAO.findById(companyId);
        if (company == null) {
            return ResponseUtil.fail(ErrorType.INVALID_COMPANY);
        }
        PayCode payCode = paycodeDAO.findById(codeId);
        if (payCode == null) {
            return ResponseUtil.fail(ErrorType.INVALID_PAY_CODE);
        }
        Integer payCodeCompanyId = payCode.getCompanyId();
        if (payCodeCompanyId != null) {
            return ResponseUtil.fail(ErrorType.USED_PAY_CODE);
        }

        PayCode entity = new PayCode();
        entity.setId(codeId);
        entity.setCompanyId(companyId);
        entity.setBindTime(DateTimeUtil.getNowTime());
        int result = paycodeDAO.update(entity);
        if (result > 0) {
            return ResponseUtil.success("绑定成功");
        } else {
            return ResponseUtil.fail(ErrorType.SYSTEM_ERROR);
        }
    }

}
